package com.sagframe.sagacity.sqltoy.plus.dao;

import com.sagframe.sagacity.sqltoy.plus.chain.ChainWrappers;
import com.sagframe.sagacity.sqltoy.plus.chain.query.LambdaChainQueryWrapper;
import com.sagframe.sagacity.sqltoy.plus.chain.query.QueryChainWrapper;
import com.sagframe.sagacity.sqltoy.plus.chain.update.LambdaChainUpdateWrapper;
import com.sagframe.sagacity.sqltoy.plus.chain.update.UpdateChainWrapper;
import com.sagframe.sagacity.sqltoy.plus.conditions.toolkit.ColumnUtils;
import com.sagframe.sagacity.sqltoy.plus.multi.MultiWrapper;
import org.sagacity.sqltoy.config.model.EntityMeta;
import com.sagframe.sagacity.sqltoy.plus.conditions.Wrapper;
import org.sagacity.sqltoy.dao.impl.LightDaoImpl;
import org.sagacity.sqltoy.dao.impl.SqlToyLazyDaoImpl;
import org.sagacity.sqltoy.exception.DataAccessException;
import org.sagacity.sqltoy.model.EntityQuery;
import org.sagacity.sqltoy.model.EntityUpdate;
import org.sagacity.sqltoy.model.Page;
import org.sagacity.sqltoy.utils.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository("sqlToyHelperDao")
public class SqlToyHelperDaoImpl extends SqlToyLazyDaoImpl implements SqlToyHelperDao {

    @Autowired
    @Lazy
    private SqlToyMultiFiledMappingStrategy multiFiledMappingStrategy;

    @Autowired
    @Lazy
    private SqlToySinFiledMappingStrategy sinFiledMappingStrategy;

    public <T> T findOne(Wrapper<T> wrapper) {
        List<T> rs = findList(wrapper);
        if (rs == null || rs.isEmpty()) {
            return null;
        }
        return rs.get(0);
    }

    @Override
    public <T> T findOne(MultiWrapper wrapper) {
        List<T> rs = findList(wrapper);
        if (rs == null || rs.isEmpty()) {
            return null;
        }
        return rs.get(0);
    }

    public <T> List<T> findList(Wrapper<T> wrapper) {
        return super.findEntity(wrapper.entityClass(), getEntityQuery(wrapper));
    }

    @Override
    public <T> List<T> findList(MultiWrapper wrapper) {
        //开始组装sql
        wrapper.assemble(multiFiledMappingStrategy);
        return super.findBySql(wrapper.getSqlSegment(), wrapper.getSqlSegmentParamMap(), wrapper.toClass());
    }

    public <T> Page<T> findPage(Wrapper<T> wrapper, Page<?> page) {
        return super.findPageEntity(page, wrapper.entityClass(), getEntityQuery(wrapper));
    }

    @Override
    public <T> Page<T> findPage(MultiWrapper wrapper, Page<?> page) {
        //开始组装sql
        wrapper.assemble(multiFiledMappingStrategy);
        return super.findPageBySql(page, wrapper.getSqlSegment(), wrapper.getSqlSegmentParamMap(), wrapper.toClass());
    }

    public <T> long delete(Wrapper<T> wrapper) {
        return super.deleteByQuery(wrapper.entityClass(), getEntityQuery(wrapper));
    }

    @Override
    public long delete(MultiWrapper multiDeleteWrapper) {
        multiDeleteWrapper.assemble(multiFiledMappingStrategy);
        return super.executeSql(multiDeleteWrapper.getSqlSegment(), multiDeleteWrapper.getSqlSegmentParamMap());
    }

    public <T> long count(Wrapper<T> wrapper) {
        return super.getCount(wrapper.entityClass(), getEntityQuery(wrapper));
    }

    @Override
    public long update(MultiWrapper multiUpdateWrapper) {
        multiUpdateWrapper.assemble(multiFiledMappingStrategy);
        return super.executeSql(multiUpdateWrapper.getSqlSegment(), multiUpdateWrapper.getSqlSegmentParamMap());
    }

    public <T> long update(Wrapper<T> updateWrapper) {
        return super.updateByQuery(updateWrapper.entityClass(), getEntityUpdate(updateWrapper));
    }

    public <T> long update(T t, Wrapper<T> wrapper) {
        return super.updateByQuery(wrapper.entityClass(), getEntityUpdate(t, wrapper));
    }

    public <T> long update(Map<String, Object> setMap, Wrapper<T> wrapper) {
        return super.updateByQuery(wrapper.entityClass(), getEntityUpdate(setMap, wrapper));
    }

    @Override
    public <T> LambdaChainQueryWrapper<T> lambdaQueryChain(Class<T> entityClass) {
        return ChainWrappers.lambdaChainWrapper(entityClass, () -> this);
    }

    @Override
    public <T> QueryChainWrapper<T> queryChain(Class<T> entityClass) {
        return ChainWrappers.chainWrapper(entityClass, () -> this);
    }

    @Override
    public <T> LambdaChainUpdateWrapper<T> lambdaUpdateChain(Class<T> entityClass) {
        return ChainWrappers.lambdaUpdateChainWrapper(entityClass, () -> this);
    }

    @Override
    public <T> UpdateChainWrapper<T> updateChain(Class<T> entityClass) {
        return ChainWrappers.updateChainWrapper(entityClass, () -> this);
    }

    /**
     * 获取查询条件对象
     *
     * @param wrapper - 查询条件
     * @param <T>
     * @return
     */
    private <T> EntityQuery getEntityQuery(Wrapper<T> wrapper) {
        //开始组装sql
        wrapper.assemble(sinFiledMappingStrategy);
        EntityQuery entityQuery = EntityQuery.create().where(wrapper.getSqlSegment());
        // fix where无参数，报org.sagacity.sqltoy.utils.SqlUtil：Parameter index out of range (1 > number of parameters, which is 0).问题
        Map<String, Object> paramMap = wrapper.getSqlSegmentParamMap();
        if (paramMap != null && !paramMap.isEmpty()) {
            entityQuery.values(wrapper.getSqlSegmentParamMap());
        }

        if (wrapper.getSelectColumns() != null && !wrapper.getSelectColumns().isEmpty()) {
            entityQuery.select(wrapper.getSelectColumns().toArray(new String[0]));
        }
        return entityQuery;
    }

    /**
     * 组装更新条件
     *
     * @param updateWrapper - 更新条件
     * @param <T>
     * @return
     */
    private <T> EntityUpdate getEntityUpdate(Wrapper<T> updateWrapper) {
        Map<String, Object> setMap = updateWrapper.getSetMap();
        return getEntityUpdate(setMap, updateWrapper);
    }

    /**
     * @param t                - 更新元数据
     * @param queryWrapper     - 更新的查询条件
     * @param forceUpdateProps - 忽略字段
     * @param <T>
     * @return
     */
    private <T> EntityUpdate getEntityUpdate(T t, Wrapper<T> queryWrapper, String... forceUpdateProps) {
        EntityUpdate entityUpdate = EntityUpdate.create();
        EntityMeta entityMeta = super.getEntityMeta(queryWrapper.entityClass());
        Iterator<Map.Entry<String, String>> iterator = entityMeta.getColumnFieldMap().entrySet().iterator();
        Map<String, Object> setParamMap = ColumnUtils.objectToObjectMap(t, forceUpdateProps);
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            Object value = setParamMap.get(entry.getKey());
            if (value != null) {
                entityUpdate.set(entry.getKey(), value);
            }
        }
        return getEntityUpdate(setParamMap, queryWrapper);
    }

    /**
     * 组装更新条件
     *
     * @param queryWrapper - 更新的查询条件
     * @param <T>
     * @return
     */
    private <T> EntityUpdate getEntityUpdate(Map<String, Object> setMap, Wrapper<T> queryWrapper) {
        if (setMap == null || setMap.size() == 0) {
            throw new DataAccessException("sqlToy plus update param can not empty");
        }
        //开始组装sql
        queryWrapper.assemble(sinFiledMappingStrategy);
        EntityUpdate entityUpdate = EntityUpdate.create();
        setMap.forEach(entityUpdate::set);
        // 当where无参数时，添加默认条件1=1，解决SqlToyDaoSupport.updateByQuery#1964的where语句中的?数量跟对应values 数组长度不一致,请检查!异常。
        String sqlSegment = queryWrapper.getSqlSegment();
        Map<String, Object> paramMap = queryWrapper.getSqlSegmentParamMap();
        if (paramMap.isEmpty()) {
            sqlSegment = StringUtil.isBlank(sqlSegment) ? "1=:DEFAULT_VALUE " : ("1=:DEFAULT_VALUE AND " + sqlSegment);
            paramMap.put("DEFAULT_VALUE", 1);
        }
        return entityUpdate.where(sqlSegment).values(paramMap);
    }
}
